package imageUtility;
import imageClassifying.EuclideanDistancer;
import imageClassifying.IDistance;

import java.util.ArrayList;
import java.util.List;



public class ImagePlacer {

	IDistance idistance;
	KNN knnUtil;
	int k;
	ImageData[][] placedImagesMatrix;
	ArrayList<ImageData> placedImagesList;
	
	
	
	public ImagePlacer(List<ImageData> imageList, int k){
		
		placedImagesMatrix = new ImageData[2*imageList.size()][2*imageList.size()];
		placedImagesList = new ArrayList<ImageData>();		
		idistance = new EuclideanDistancer();
		knnUtil = new KNN(idistance);
		this.k =k;
		initDataSet(imageList);
		System.out.println("progress - finished init of data set");
		placeAllImages(imageList);
		System.out.println("progress - finished placing images");
	}

	private void initDataSet(List<ImageData> imageList) {
		if (imageList.size() < 4){
			System.err.println("Images data set to small");
			return;
		}
		ImageData image1,image2,image3,image4;
		image1 = imageList.get(0);
		imageList.remove(0);
		image2 = imageList.get(0);
		imageList.remove(0);
		image3 = imageList.get(0);
		imageList.remove(0);
		image4 = imageList.get(0);
		imageList.remove(0);
		image1.setXY(imageList.size()-1, imageList.size()-1);
		placedImagesMatrix[(int)image1.getX()][(int)image1.getY()] = image1;
		image2.setXY(imageList.size()-1, imageList.size());
		placedImagesMatrix[(int)image2.getX()][(int)image2.getY()] = image2;
		image3.setXY(imageList.size(), imageList.size()-1);
		placedImagesMatrix[(int)image3.getX()][(int)image3.getY()] = image3;
		image4.setXY(imageList.size(), imageList.size());
		placedImagesMatrix[(int)image4.getX()][(int)image4.getY()] = image4;
		placedImagesList.add(image1);
		placedImagesList.add(image2);
		placedImagesList.add(image3);
		placedImagesList.add(image4);
	}

	private void placeAllImages(List<ImageData> imagesList){
		
		for (ImageData image : imagesList)
			placeImage(image);
	}
	
	public ArrayList<ImageData> getPlacedImagesList() {
		
		return placedImagesList;
	}
	
	public ImageData[][] getPlacedImagesMatrix() {
		
		return placedImagesMatrix;
	}

	
	private void placeImage(ImageData newImage){
		
		knnUtil.regressImage(newImage, placedImagesList, k);
		
		PairXY gridLocation = newImage.getXY().roundXY();
		ImageData oldImage = placedImagesMatrix[(int)gridLocation.getX()][(int)gridLocation.getY()];
		if (oldImage == null)
		{
			placedImagesMatrix[(int)gridLocation.getX()][(int)gridLocation.getY()] = newImage;
		}
		else
		{
			chooseWinnerAndAdaptMatrix(newImage, oldImage);					
		}
		
		placedImagesList.add(newImage);
	}

	private void chooseWinnerAndAdaptMatrix(ImageData newImage, ImageData oldImage) {
		
		ImageData upNeighbore = placedImagesMatrix[(int)newImage.getX()][(int)newImage.getY() + 1];
		ImageData downNeighbore = placedImagesMatrix[(int)newImage.getX()][(int)newImage.getY() - 1];
		ImageData rightNeighbore = placedImagesMatrix[(int)newImage.getX() + 1][(int)newImage.getY()];
		ImageData leftNeighbore = placedImagesMatrix[(int)newImage.getX() - 1][(int)newImage.getY()];
		
		CompetitionResultsInfo results = chooseWinnerImage(oldImage, newImage, upNeighbore, downNeighbore, rightNeighbore, leftNeighbore);
		
		placedImagesMatrix[(int)oldImage.getX()][(int)oldImage.getY()] = results.getWinnerImage();
		
		// push the loser image to the direction specified
		PushImageToDirection(results.getLoserImage(),results.getNeagteDirection());
			
	}


	private void PushImageToDirection(ImageData image, Direction direction) {
		

		ImageData upNeighbore = placedImagesMatrix[(int)image.getX()][(int)image.getY() + 1];
		ImageData downNeighbore = placedImagesMatrix[(int)image.getX()][(int)image.getY() - 1];
		ImageData rightNeighbore = placedImagesMatrix[(int)image.getX() + 1][(int)image.getY()];
		ImageData leftNeighbore = placedImagesMatrix[(int)image.getX() - 1][(int)image.getY()];
		ImageData topLeft = placedImagesMatrix[(int)image.getX()-1][(int)image.getY() + 1];
		ImageData topRight = placedImagesMatrix[(int)image.getX()+1][(int)image.getY() +1 ];
		ImageData bottomLeft = placedImagesMatrix[(int)image.getX() - 1][(int)image.getY()-1];
		ImageData bottomRight = placedImagesMatrix[(int)image.getX() + 1][(int)image.getY()-1];
		
		if (upNeighbore == null) {
			placedImagesMatrix[(int)image.getX()][(int)image.getY() + 1] = image;
			image.setXY((int)image.getX(),(int)image.getY() + 1);
			return;
		}
		if (downNeighbore == null) {
			placedImagesMatrix[(int)image.getX()][(int)image.getY() - 1] = image;
			image.setXY((int)image.getX(),(int)image.getY() - 1);
			return;
		}
		if (rightNeighbore == null) {
			placedImagesMatrix[(int)image.getX()+1][(int)image.getY()] = image;
			image.setXY((int)image.getX()+1,(int)image.getY());
			return;
		}
		if (leftNeighbore == null) {
			placedImagesMatrix[(int)image.getX()-1][(int)image.getY()] = image;
			image.setXY((int)image.getX()-1,(int)image.getY());
			return;
		}
		if (topLeft == null) {
			placedImagesMatrix[(int)image.getX()-1][(int)image.getY() + 1] = image;
			image.setXY((int)image.getX()-1,(int)image.getY() + 1);
			return;
		}
		if (topRight == null) {
			placedImagesMatrix[(int)image.getX()+1][(int)image.getY() + 1] = image;
			image.setXY((int)image.getX()+1,(int)image.getY() + 1);
			return;
			
		}
		if (bottomLeft == null) {
			placedImagesMatrix[(int)image.getX()-1][(int)image.getY() - 1] = image;
			image.setXY((int)image.getX()-1,(int)image.getY() - 1);
			return;
			
		}
		if (bottomRight == null) {
			placedImagesMatrix[(int)image.getX()+1][(int)image.getY() - 1] = image;
			image.setXY((int)image.getX()+1,(int)image.getY() - 1);
			return;
			
		}

		
		image.getXY().push(direction);
		int xVal = (int)image.getXY().getX();
		int yVal = (int)image.getXY().getY();
		ImageData newLoserImage = placedImagesMatrix[xVal][yVal];
		placedImagesMatrix[xVal][yVal] = image;
		// continue pushing recursively the interrupting image
		if (newLoserImage != null)
			PushImageToDirection(newLoserImage, direction);
		
	}

	private CompetitionResultsInfo chooseWinnerImage(ImageData oldImage,ImageData newImage, 
												 ImageData upNeighbore, ImageData downNeighbore,
											     ImageData rightNeighbore, ImageData leftNeighbore) {
		
		Direction closestNeighborDirection1 = Direction.DOWN, closestNeighborDirection2 = Direction.DOWN; 
		double currDistance;
		
		////// calc for the new image
		double closestNeighborDistance1 = Double.MAX_VALUE;
		if ((upNeighbore!=null)&&((currDistance = idistance.calculateDistance(newImage.getVector(), upNeighbore.getVector())) < closestNeighborDistance1))
		{
			closestNeighborDistance1 = currDistance;
			closestNeighborDirection1 = Direction.UP;
		}
		if ((downNeighbore!=null)&&((currDistance = idistance.calculateDistance(newImage.getVector(), downNeighbore.getVector())) < closestNeighborDistance1))
		{
			closestNeighborDistance1 = currDistance;
			closestNeighborDirection1 = Direction.DOWN; 			
		}
		if ((rightNeighbore!=null)&&((currDistance = idistance.calculateDistance(newImage.getVector(), rightNeighbore.getVector())) < closestNeighborDistance1))
		{
			closestNeighborDistance1 = currDistance;
			closestNeighborDirection1 = Direction.RIGHT; 			
		}
		if ((leftNeighbore!=null)&&((currDistance = idistance.calculateDistance(newImage.getVector(), leftNeighbore.getVector())) < closestNeighborDistance1))
		{
			closestNeighborDistance1 = currDistance;
			closestNeighborDirection1 = Direction.LEFT; 			
		}
		
		////// calc for the old image
		double closestNeighborDistance2 = Double.MAX_VALUE;

		if ((upNeighbore!=null)&&((currDistance = idistance.calculateDistance(oldImage.getVector(), upNeighbore.getVector())) < closestNeighborDistance2))
		{
			closestNeighborDistance2 = currDistance;
			closestNeighborDirection2 = Direction.UP;
		}
		if ((downNeighbore!=null)&&((currDistance = idistance.calculateDistance(oldImage.getVector(), downNeighbore.getVector())) < closestNeighborDistance2))
		{
			closestNeighborDistance2 = currDistance;
			closestNeighborDirection2 = Direction.DOWN; 			
		}
		if ((rightNeighbore!=null)&&((currDistance = idistance.calculateDistance(oldImage.getVector(), rightNeighbore.getVector())) < closestNeighborDistance2))
		{
			closestNeighborDistance2 = currDistance;
			closestNeighborDirection2 = Direction.RIGHT; 			
		}
		if ((leftNeighbore!=null)&&((currDistance = idistance.calculateDistance(oldImage.getVector(), leftNeighbore.getVector())) < closestNeighborDistance2))
		{
			closestNeighborDistance2 = currDistance;
			closestNeighborDirection2 = Direction.LEFT; 			
		}
		
		if (closestNeighborDistance1 < closestNeighborDistance2)
			return new CompetitionResultsInfo(newImage,oldImage, closestNeighborDirection1);
		else 
			return new CompetitionResultsInfo(oldImage, newImage, closestNeighborDirection2);
	}


	public class CompetitionResultsInfo {
		
		Direction direction;
		ImageData winner, loser;

		public CompetitionResultsInfo(ImageData winnerImage,ImageData loserImage, Direction direction){
			winner = winnerImage;
			loser = loserImage;
			this. direction = direction;
		}
		
		public Direction getDirection() {
			return direction;
		}
		
		public Direction getNeagteDirection() {
			switch (direction){
			case UP: 
					return Direction.DOWN;
			case DOWN: 
					return Direction.UP;
			case RIGHT: 
					return Direction.LEFT;
			case LEFT: 
					return Direction.RIGHT;				
			}
			return Direction.RIGHT; // we won't ger here
		}
		
		public ImageData getWinnerImage() {
			return winner;
		}
		
		public ImageData getLoserImage() {
			return loser;
		}
		
	}
		
}


	

